
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj.fieldValidator;
using Swaf.BizObj.basic;
using Swaf.BizObj.xml;
using Swaf.Call;
using Swaf.Config;
using Swaf.Container;
using Swaf.Script;

namespace Swaf.BizObj
{
	/// <summary>
	/// Main class for creating the various types of bizobj instances including bizobj,
	/// bizobjlist, and bizobjsource.
	/// </summary>
	public class BizObjManager : MarshalByRefObject, IBizObjManager, IConfigPartner
	{
		protected static bool s_isStartup = false;
		protected static IBizObjIterator s_emptyIterator = new IteratorSet();

		public static string NullValueIndicatorName = "~NULL";
		public static bool SuggestAttributesForXML = true;
		public static bool NullAsElementAttribute = false;
		public static bool XmlOutSetOnly = true;
		public static string BizObjCryptoKeyName = "";

		protected static Regex s_bizObjListFieldPattern = new Regex(@"List\s*\((.+)\)",RegexOptions.IgnoreCase);

		protected IMap m_bizobjDefs = null;
		protected IMap m_startUp_bizobjDefs = null;

		protected ArrayList m_bizobjTypeNames = new ArrayList();
		protected IList m_validatorPrototypes = new ArrayList();
		private ScriptManager m_scripts;
		protected ICall m_dynamicBizObjRegister = null;
		protected Dictionary<string, IBizObjIterator> m_iterators = null;

		private UnknownReferences m_startupUnknownReferences = new UnknownReferences();

		protected const string BASE_BIZOBJ_NAME = "BizObj";

		public BizObjManager(IApplication app)
		{
			//Intentionally left blank.  This constructor is needed to be an IConfigPartner
		}

		public IBizObjDefinition getBizObjDefinition(string bizObjTypeName)
		{
			IBizObjDefinition def = BizObjDefinitions[bizObjTypeName] as IBizObjDefinition;
			if (def == null && dynamicBizObjRegister != null)
			{
			    try
			    {
			        def = m_dynamicBizObjRegister.call(bizObjTypeName) as IBizObjDefinition;
			    }
			    catch (Exception e)
			    {
                    // purposefully going to eat any exceptions here

                    if (!bizObjTypeName.StartsWith("List("))
                    {
                        Boolean suppressNotFoundLog = false;
                        String suppressNotFoundMacro = Application.currentApp.resMgr.resolve("$SuppressBizObjNotRegLog$");
                        if (suppressNotFoundMacro != "$SuppressBizObjNotRegLog$")
                        {
                            try
                            {
                                suppressNotFoundLog = Convert.ToBoolean(suppressNotFoundMacro);
                            }
                            catch
                            {
                                // Just trap error and let the default value continue
                            }
                        }

                        // Only log this if it wasn't a List(bizobjtype) look up because the List() can occur
                        // frequently and isn't inherently damaging
                        if (!suppressNotFoundLog)
                        {
                            Application.currentApp.Log.println(string.Format("Dynamic BizObj Registration failed with the following exception: {0}", e.ToString()));
                        }
                    }
                }
			}
			return def;
		}

		public ICall dynamicBizObjRegister
		{
			get { return m_dynamicBizObjRegister; }
			set { m_dynamicBizObjRegister = value; }
		}

		#region IBizObjManager Members
		public IBizObjSource createSource(SourceType type, string info)
		{
			return createSource(type, info, null);
		}

		public IBizObjSource createSource(SourceType type, string info, string extraInfo)
		{
			IBizObjSource source = null;
			switch (type)
			{
				case SourceType.XmlSource:
					source = new XmlBizSource(info);
					break;
				case SourceType.BasicSource:
					source = extraInfo == null ? new FullBizObjSource(info) : new FullBizObjSource(info, extraInfo);
					break;
			}

			return source;
		}

		/// <summary>Creates a BusinessObject of the specified type.
		/// The created object might have invalid fields (that is, the fields'
		/// validators might indicate that the fields are invalid), because the
		/// object is in a conceptually "empty" state.</summary>
		/// <remarks>
		/// Postcondition:
		/// <c>
		/// BusinessObject x = BusinessObjects.create(type);
		/// x.type().equals(type);
		/// </c>
		/// </remarks>
		public IBizObj create(string type)
		{
			IBizObjDefinition info = getBizObjDefinition(type);
			if (info == null)
				throw new BadBizObjTypeException(Application.globalApp.resMgr.getMessage("BusinessObject.create", type));
			return FullBizObj.createInstance(info);
		}

		public IBizObjDefinition getDefinition(string type)
		{
			return getBizObjDefinition(type);
//			IBizObjDefinition def = BizObjDefinitions[type] as IBizObjDefinition;
//			if (def == null)
//				throw new BadBizObjTypeException(Application.globalApp.resMgr.getMessage("BusinessObject.create", type));
//			return def;
		}

		public IBizObjDefinition checkForDefinition(string type)
		{
            return getBizObjDefinition(type);
		}

		public IList Definitions
		{
			get { return ArrayList.ReadOnly(m_bizobjTypeNames); }
		}
		#endregion

		#region IConfigPartner Members
		public void registerForConfig(IConfigManager config)
		{
			s_isStartup = true;

			BaseBizObj.resetFactoryReferences();
			FullBizObj.resetFactoryReferences();

			config.registerHandler("...<BizObjXmlOptions>", new BizObjXmlOptionsConfigHandler(this));
			config.registerHandler("...<BusinessObject>", new BizObjConfigHandler(this));
			config.registerHandler("...<BusinessObject> <Method>", new BizObjMethodConfigHandler(this));
			config.registerHandler("...<BusinessObject> <Field>", new BizObjFieldConfigHandler(this));
			config.registerHandler("...<FieldValidators> <Class>", new FieldValidatorConfigHandler(this));
			config.registerHandler("...<ValidatorConfig> <PatternValidator>", new PatternValidatorConfigHandler(this));
			config.registerHandler("...<DynamicBizObjRegister>", new DynamicBizObjRegistrationHandler(this));
		}

		public void checkConfigStatus()
		{
			s_isStartup = false;

			bool stillHasUnknownTypes = m_startupUnknownReferences.Count > 0;
			if (stillHasUnknownTypes)
				throw new BaseException(m_startupUnknownReferences.GetUnknownErrorInfo());

			if (m_scripts != null)
				m_scripts.completeInitialization();
		}
		#endregion

		protected ScriptManager scripts
		{
			get
			{
				if (m_scripts == null)
					m_scripts = new ScriptManager();
				return m_scripts;
			}
		}
        
		protected internal string defineBizObjType(string type, string key, string extendsType, string injectsType, IConfigElement element, string fileName, string mapExtraInfo)
		{
		    IBizObjDefinition boDef = null;
            string msgs = defineBizObjType(type, key, extendsType, injectsType, element, fileName, mapExtraInfo, out boDef);
		    if (boDef != null)
		    {
		        // put in the bizobj mgr cache
				if (mapExtraInfo != null)
				{
				    BizObjDefinitions.put(type, boDef, mapExtraInfo);
				}
				else
				{
				    BizObjDefinitions[type] = boDef;
				}
		        
				m_bizobjTypeNames.Add(type);
		    }
		    
		    return msgs;
		}

        protected internal string defineBizObjType(string type, string key, string extendsType, string injectsType, IConfigElement element, string fileName, string mapExtraInfo, out IBizObjDefinition bizObjDef)
		{
            // Default set it to null
            bizObjDef = null;
            
			StringBuilder buff = new StringBuilder();

			if (injectsType != null && extendsType != null)
				buff.Append(String.Format("BizObj definition extends '{0}' and injects '{1}'.  When injecting fields into an existing bizobj definition only inject should be used and no extends is defined.",
					extendsType, injectsType));
			if(injectsType != null && type != null)
				buff.Append(String.Format("Type '{0}' injects '{1}'.  When injecting fields into an existing bizobj definition only inject should be used and no type is defined.",
					type, injectsType));
			if (injectsType == null && type == null)
				buff.Append(String.Format("BizObj definition must define either a type attribute or an injects attribute. BizObj definition in File {0} on Line {1} defines neither",
					fileName, element.LineNumber));
			if (injectsType != null)
			{
				IBizObjDefinition injectsDef = getBizObjDefinition(injectsType);
				if (injectsDef == null)
				{
					//Create a new bizobj definition for the injected fields to be placed.  If the injected type is encountered
					//later, this fields will be injected into it then.
					injectsDef = new BizObjDefinition(injectsType, true);
					m_startupUnknownReferences.addUnknownTypeReference(injectsType, injectsDef, element, fileName);
					BizObjDefinitions[injectsType] = injectsDef;
				}
			}
			else if (type != null)
			{
			    // TODO: have to resolve what happens here on looking up the typeDef, since if this was
			    // loaded with a dynamic register, we can still be in the dynamic process and we don't want to 
			    // make another call that will load the bizobjdef in that case
                BizObjDefinition typeDef = BizObjDefinitions[type] as BizObjDefinition;
				if (typeDef != null && !typeDef.IsInjectedFieldsOnly)
					buff.Append(String.Format("Type '{0}' is being redefined.  If injection is desired, use the 'inject' attribute rather then 'type'",
						type));
				else
				{
					if (typeDef == null)
						typeDef = new BizObjDefinition(type, key);
					else
					{
						//The bizobj definition already exists because it was injected into first.  Fix it up....
						typeDef.setKeyFields(key);
						typeDef.IsInjectedFieldsOnly = false;
					}
					if(extendsType == null)
						extendsType = (type == BASE_BIZOBJ_NAME) ? null : BASE_BIZOBJ_NAME;
					BizObjDefinition extendsDef = null;
					if (extendsType != null)
					{
						extendsDef = getBizObjDefinition(extendsType) as BizObjDefinition;
						if (extendsDef == null)
						{
							m_startupUnknownReferences.addUnknownTypeReference(extendsType, type, null, element, fileName);
						}
						else
						{
							typeDef.setParent(extendsDef);
							if(extendsDef.IsInjectedFieldsOnly)
								m_startupUnknownReferences.addUnknownTypeReference(extendsType, type, null, element, fileName);
						}
					}
				}

			    // Set the resulting value
                bizObjDef = typeDef;
			}

			return buff.Length == 0?null:buff.ToString();
		}

		protected internal void bizobjDefinitionComplete(string bizobjType)
		{
			BizObjDefinition typeDef = BizObjDefinitions[bizobjType] as BizObjDefinition;
			if(typeDef != null && !typeDef.IsInjectedFieldsOnly)
				m_startupUnknownReferences.fixUnknownReferences(this, typeDef);
		}

		protected internal string defineField(string bizObjType, string name, string type, string validators,
			string aggregate, string defaultValue, IConfigElement element, string fileName)
		{
            BizObjDefinition bizObjDef = getBizObjDefinition(bizObjType) as BizObjDefinition;
            return defineField(bizObjDef, name, type, validators, aggregate, defaultValue, element, fileName);
        }
        
        protected internal string defineField(IBizObjDefinition bizObjDef, string name, string type, string validators,
			string aggregate, string defaultValue, IConfigElement element, string fileName)
		{
            Debug.Assert(bizObjDef != null);
            
            StringBuilder buff = new StringBuilder();

			IClassFactory factory = Application.globalApp.factories["BizObjBizFieldTypes"];
			BizFieldType fieldType = BizFieldType.NetClass;
			object defaultInstance = defaultValue;

			//If the type is contained as a nick-name within the field factory, we know we have a simple .net type because
			//bizobj names are not placed in it, and factory patterns are not used for basic .net types.
			//However, if it is not contained as a simple nick-name it is either a bizobj or bizobjlist.
			if (!factory.entries.Contains(type))
			{
				//cannot find the value in the bizobj field factory, so it will be checked as a bizobj/bizobjlist
				BizObjDefinition bizObjField = getBizObjDefinition(type) as BizObjDefinition;
				fieldType = BizFieldType.BizObj;
				if (bizObjField == null)
				{
					Match m = s_bizObjListFieldPattern.Match(type);
					if (m.Success)
					{
						string fieldBizObjType = m.Groups[1].Value;
						fieldType = BizFieldType.BizObjList;
						bizObjField = getBizObjDefinition(fieldBizObjType) as BizObjDefinition;
						defaultInstance = FullBizObjList.createInstance("",bizObjField);
					    
						if (bizObjField == null)
						{
							//The code matched the List() regex, so we know the field type is a bizobjlist.
							//hopefully, the type will be resolved as processing continues.  This will be
							//check at the completion of startup.
                            m_startupUnknownReferences.addUnknownTypeReference(fieldBizObjType, bizObjDef.Name, name, element, fileName);
						}
					}
					else
					{
						//If the code cannot match the List() regex, we have an unknown field type on our hands.
						//For now, keep processing, assuming its a bizobj field (that is what the field will be marked
						//as).  At startup completion this will be re-evaluated.
                        m_startupUnknownReferences.addUnknownTypeReference(type, bizObjDef.Name, name, element, fileName);
						defaultInstance = FullBizObj.createInstance(bizObjField);
					}
				}
				else
				{
					//A bizobj definition was found based on the name.  This field is definately a bizobj.
					defaultInstance = FullBizObj.createInstance(bizObjField);
				}
			}
			else
				try
				{
					if (defaultValue != null)
					{
						if (defaultValue.Length == 0)
							defaultInstance = factory.createFromNickName(type);
						else
							defaultInstance = factory.createFromNickName(type, defaultValue);
					}
				}
				catch (ClassFactoryCreateException e)
				{
					Application.currentApp.Log.println(e.Message);
                    m_startupUnknownReferences.addUnknownTypeReference(type, bizObjDef.Name, name, element, fileName);
				}

			try
			{
				BizObjField field = ((BizObjDefinition) bizObjDef).addField(name, fieldType, defaultInstance, type) as BizObjField;
				Debug.Assert(field != null);
				field.Validators = parseValidators(validators);
			}
			catch (ParseError e)
			{
                buff.Append(String.Format("Unknown validator '{0}' defined on '{1}.{2}'", e.Message, bizObjDef.Name, name));
			}
			
			return buff.Length == 0 ? null : buff.ToString();
		}
		protected internal string defineFieldDecorator(string bizObjType, string fieldName, string decoratorName, string decoratorValue)
		{
            IBizObjDefinition bizObjDef = getBizObjDefinition(bizObjType);
            return defineFieldDecorator(bizObjDef, fieldName, decoratorName, decoratorValue);
        }

        protected internal string defineFieldDecorator(IBizObjDefinition bizObjDef, string fieldName, string decoratorName, string decoratorValue)
		{
			StringBuilder buff = new StringBuilder();

			Debug.Assert(bizObjDef != null);
			BizObjField field = bizObjDef.getField(fieldName, true) as BizObjField;
			Debug.Assert(field != null);

			IClassFactory decoratorFactory = Application.globalApp.factories["BizObjFieldDecorators"];
			IDecorator decorator = decoratorFactory
				.createFromNickName(decoratorName, decoratorName, decoratorValue, field) as IDecorator;
			if (decorator != null)
				field.addDecorator(decorator);
			else
				buff.Append(String.Format("Unknown decorator '{0}' specified on '{1}.{2}'.", decoratorName, bizObjDef.Name, field.Name));

			return buff.Length == 0 ? null : buff.ToString();
		}
		
        protected internal string defineMethod(string bizObjName, string name, string language, string getScript, string parameters, string setScript)
		{
            IBizObjDefinition bizObjType = getBizObjDefinition(bizObjName);
			return defineMethod(bizObjType, name, language, getScript, parameters, setScript);
        }

		protected internal string defineMethod(IBizObjDefinition bizObjType, string name, string language, string getScript, string parameters, string setScript)
		{
			StringBuilder buff = new StringBuilder();
			IClassFactory factory = Application.globalApp.factories["BizObjBizFieldTypes"];
			StringBuilder paramBuff = new StringBuilder();
			paramBuff.Append("self,app");
			if (parameters != null && parameters.Length > 0)
				paramBuff.Append(",");
			paramBuff.Append(parameters);
			string[] realGetParams = paramBuff.ToString().Split(',');
			string[] realSetParams = { "self", "app", "val" };

			string getScriptName = getScript == null?null:bizObjType.Name + "_" + name + "_get";
			string setScriptName = setScript == null?null:bizObjType.Name + "_" + name + "_set";
			try  
			{
				if(getScript != null)
					scripts.defineFunction(language, getScriptName, realGetParams, getScript);
				if(setScript != null)
					scripts.defineFunction(language, setScriptName, realSetParams, setScript);
			}
			catch (Exception e)
			{
				buff.Append(e.Message);
			}
			BizFieldType fieldType = BizFieldType.Method;
			object methodNode = factory.createFromNickName("ScriptMethod", scripts, language, getScriptName, realGetParams, setScriptName, realSetParams);
			((BizObjDefinition) bizObjType).addField(name, fieldType, methodNode, methodNode.GetType().Name);

			return buff.Length == 0 ? null : buff.ToString();
		}
		/// <summary>Registers the given <em>Pattern</em> and prototype object in the <c>prototypes_</c> list.
		/// </summary>
		/// <returns><c>null</c> on success, or a human-readable description of what went wrong on error.
		/// 
		/// </returns>
		protected internal string registerPrototype(string pattern, FieldValidator proto)
		{
			string error = Application.globalApp.resMgr.patternErrorMessage(pattern);
			if (error != null)
			{
				Application.currentApp.Log.println("Class " + proto.GetType().FullName + " uses pattern /" + pattern + "/ with " + error);
				return error;
			}
			proto.setPatternMatch(pattern, Application.globalApp.resMgr.knownGoodPattern(pattern));
			m_validatorPrototypes.Add(proto);
			return null;
		}

		/// <summary>Parses a string containing zero or more validators, separated by semicolons.
		/// </summary>
		/// <param name="validators">The input string containing the zero-or-more validators.
		/// </param>
		/// <returns>A FieldValidator that corresponds to the validators in the <c>validators</c> string,
		/// or null if the string didn't contain any validators.
		/// @throws ParseError when there was an error while parsing <c>validators</c>.
		/// 
		/// </returns>
		protected internal IList parseValidators(string validators)
		{
			IList ans = new ArrayList();
			string[] vals = (validators.Length == 0)?new string[0]:validators.Split(';');

			foreach (string valStr in vals)
			{
				string validatorStr = valStr.Trim();
				// Find the "best" validator (the one whose match is the longest):
				FieldValidator v = null;
				int len = -1;
				string a = null, b = null, c = null;
				foreach (FieldValidator tmp in m_validatorPrototypes)
				{
					Regex r = tmp.regexp;
					string s = r.ToString();
					Match m = r.Match(validatorStr);
					if (m.Success && m.Length >= len)
					{
						v = tmp;
						len = m.Length;
						a = m.Groups[1].Value;
						b = m.Groups[2].Value;
						c = m.Groups[3].Value;
					}
				}
				if (v == null)
				{
					if (Application.globalApp.resMgr.patternErrorMessage("{" + validatorStr + "}") == null)
						v = new PatternValidator(validatorStr, "{" + validatorStr + "}", "like a " + validatorStr);
					else
						throw new ParseError("Unknown validator: " + validatorStr);
				}
				if (!(v is OptionalValidator))
					ans.Add(v.create(validatorStr, a, b, c));
			}

			return ans;
		}

		protected IMap BizObjDefinitions
		{
			get
			{
				lock (this)
				{
					if (s_isStartup)
					{
						if (m_startUp_bizobjDefs == null)
						{
							// Create bizobjdefs from the Factory provided for startup purposes
							IClassFactory dicFactory = Application.globalApp.factories["Dictionaries"];
							Debug.Assert(dicFactory != null);
							
							m_startUp_bizobjDefs = dicFactory.createFromNickName("bizobj_versioning") as IMap;
						}

						return m_startUp_bizobjDefs;
					}
					else
					{
						if (m_bizobjDefs == null)
						{
							// Create the bizobj from the factory
							IClassFactory dicFactory = Application.globalApp.factories["Dictionaries"];
							Debug.Assert(dicFactory != null);

							m_bizobjDefs = dicFactory.createFromNickName("bizobj_versioning") as IMap;

							if (m_bizobjDefs.GetType().Equals(m_startUp_bizobjDefs.GetType()))
							{
								// Type hasn't changed that was used for startup so don't bother copying
								m_bizobjDefs = m_startUp_bizobjDefs;;
							}
							else
							{
								// Type has changed so need to copy to the new type
								// NOTE: This is doing a straight copy, if there was any 'extrainfo' in the
								//		 startup dictionary that is being lost.  It is assumed that only a bare
								//		 minimum bizobjs should be declared at startup, and those that are declared
								//		 at startup are static biz objs and not dynamically loaded bizobjs.  Stitic
								//		 biz objs don't specify extra info for timeout purposes
								foreach (DictionaryEntry entry in m_startUp_bizobjDefs)
								{
									m_bizobjDefs.Add(entry.Key, entry.Value);
								}
								m_startUp_bizobjDefs.Clear();
							}

							m_startUp_bizobjDefs = null;
						}

						Debug.Assert(m_bizobjDefs != null);
						return m_bizobjDefs;
					}
				}			
			}
		}

		#region Config Handlers
		protected class BizObjXmlOptionsConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "nullName", "nullAsElementAttribute", "suggestAttributes", "setOnly", "cryptoKeyName" };
			private BizObjManager m_mgr;
			public BizObjXmlOptionsConfigHandler(BizObjManager mgr)
			{
				m_mgr = mgr;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				configInfo.checkIllegalAttributes(s_legalAttributes);
				BizObjManager.NullValueIndicatorName = configInfo.getAttribute("nullName", "NULL");
				BizObjManager.SuggestAttributesForXML = bool.Parse(configInfo.getAttribute("suggestAttributes", "true"));
				BizObjManager.NullAsElementAttribute = bool.Parse(configInfo.getAttribute("nullAsElementAttribute", "false"));
				BizObjManager.XmlOutSetOnly = bool.Parse(configInfo.getAttribute("setOnly", "true"));
				BizObjManager.BizObjCryptoKeyName = configInfo.getAttribute("cryptoKeyName", "");
				Application.currentApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL, "Defined BizObj Xml Output Options:  nullName={0}, suggestAttributes={1}, nullAsElementAttribute={2}, setOnly={3}, cryptoKeyName={4}", BizObjManager.NullValueIndicatorName, BizObjManager.SuggestAttributesForXML, BizObjManager.NullAsElementAttribute, BizObjManager.XmlOutSetOnly, BizObjManager.BizObjCryptoKeyName);
			}
		}

		protected class BizObjConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "type", "extends", "key", "injects"};
			private BizObjManager m_mgr;
			public BizObjConfigHandler(BizObjManager mgr)
			{
				m_mgr = mgr;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string type = configInfo.getAttribute("type");
				string key = configInfo.getAttribute("key");
				string extendsType = configInfo.getAttribute("extends");
				string injectsType = configInfo.getAttribute("injects");
				Application.globalApp.Log.println(Application.VERBOSE_STARTUP_LOG_LEVEL, "Defined BizObj {0} which extends {1}", type, extendsType);

				string message = m_mgr.defineBizObjType(type, key, extendsType, injectsType, configInfo, (string)configInfo.Context["filename"], null);
				if (message != null)
					xmlError(configInfo.Context, message);
				configInfo.Context["bizObjType"] = type==null?injectsType:type;
			}

			public override void executeAfter(IConfigElement configInfo)
			{
				m_mgr.bizobjDefinitionComplete(configInfo.Context["bizObjType"].ToString());
			}
		}

		protected class BizObjMethodConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "name", "language", "parameters" };
			public BizObjMethodConfigHandler(BizObjManager enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				IList unknowns = configInfo.checkUnknownAttributes(s_legalAttributes);
				string bizObjType = (string)configInfo.Context["bizObjType"];
				string name = configInfo.getAttribute("name");
				string parameters = configInfo.getAttribute("parameters", "");
				string language = configInfo.getAttribute("language", "jscript");
				XmlElement getEl = configInfo.Element.SelectSingleNode("Get") as XmlElement;
				XmlElement setEl = configInfo.Element.SelectSingleNode("Set") as XmlElement;
				string getScript = null, setScript = null;
				if (getEl == null && setEl == null)
					getScript = configInfo.getChildText().Trim();
				else
				{
					if (getEl != null)
						getScript = getEl.InnerText.Trim();
					if(setEl != null)
						setScript = setEl.InnerText.Trim();
				}

				if (getScript != null && getScript.StartsWith("<![CDATA["))
					getScript = getScript.Substring(9, getScript.Length - 12);
				if (setScript != null && setScript.StartsWith("<![CDATA["))
					setScript = setScript.Substring(9, setScript.Length - 12);
				
				string err = enclosingInstance.defineMethod(bizObjType, name, language, getScript, parameters, setScript);
				if (err != null)
					xmlError(configInfo.Context, err);

				if (unknowns.Count > 0)
				{
					//Attempts to use unknown attributes at decorators ontop of this field.
					foreach (string[] info in unknowns)
						enclosingInstance.defineFieldDecorator(bizObjType, name, info[0], info[1]);
				}
				
			}
			private BizObjManager enclosingInstance;
		}

		protected class BizObjFieldConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "name", "type", "validator", "aggregate" };
			private BizObjManager m_mgr;
			public BizObjFieldConfigHandler(BizObjManager mgr)
			{
				this.m_mgr = mgr;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				string bizObjType = (string)configInfo.Context["bizObjType"];
				IList unknowns = configInfo.checkUnknownAttributes(s_legalAttributes);
				string name = configInfo.getAttribute("name");
				string newValue = null;
				if (configInfo.Element.GetElementsByTagName("NULL").Count == 0)
				{
					newValue = configInfo.getChildText();
					if (newValue == null)
						newValue = "";
				}
				string type = configInfo.getAttribute("type", "String");
				string validator = configInfo.getAttribute("validator", "");
				string aggregate = configInfo.getAttribute("aggregate", "true");

				string err = m_mgr.defineField(bizObjType, name, type, validator, aggregate, newValue, configInfo, (string)configInfo.Context["filename"]);
				if (err != null)
					xmlError(configInfo.Context, err);

				if (unknowns.Count > 0)
				{
					//Attempts to use unknown attributes at decorators ontop of this field.
					foreach (string[] info in unknowns)
						m_mgr.defineFieldDecorator(bizObjType, name, info[0], info[1]);
				}
			}
		}

		protected class FieldValidatorConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { };
			public FieldValidatorConfigHandler(BizObjManager enclosingInstance)
			{
				this.enclosingInstance = enclosingInstance;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string className = configInfo.getChildText();
				Cmn.callStatic(className, "register", new Type[] { typeof(IApplication) }, new Object[] { Application.currentApp });
			}
			private BizObjManager enclosingInstance;
		}

		protected class PatternValidatorConfigHandler : ConfigHandler
		{
			protected static string[] s_legalAttributes = new string[] { "pattern", "example", "replace", "descrip" };
			private BizObjManager m_mgr;
			public PatternValidatorConfigHandler(BizObjManager mgr)
			{
				m_mgr = mgr;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = Application.currentApp;
				configInfo.checkIllegalAttributes(s_legalAttributes);
				string pattern = configInfo.getAttribute("pattern");
				string example = configInfo.getAttribute("example");
				string replace = configInfo.getAttribute("replace");
				string descrip = configInfo.getAttribute("descrip");
				string err = app.resMgr.patternErrorMessage(replace);
				if (err != null)
					xmlError(configInfo.Context, err + " in replace=\"" + replace + "\"");

				
				err = m_mgr.registerPrototype(pattern, new PatternValidator(example, replace, descrip));
				if (err != null)
					xmlError(configInfo.Context, err + " in pattern=\"" + pattern + "\"");
			}
		}

		/// <summary>
		/// Configuration handler to configure ICall that will dynamically load bizobj 
		/// definitions.
		/// </summary>
		private class DynamicBizObjRegistrationHandler : ConfigHandler
		{
			private BizObjManager m_mgr;

			public DynamicBizObjRegistrationHandler(BizObjManager mgr)
			{
				m_mgr = mgr;
			}
			public override void executeBefore(IConfigElement configInfo)
			{
				IApplication app = (IApplication)configInfo.Context["app"];
				IClassFactory m_callFactory = Application.currentApp.factories["CallTypes"];
				foreach (XmlNode node in configInfo.Element.ChildNodes)
					if (node is XmlElement)
					{
						m_mgr.dynamicBizObjRegister = (ICall)m_callFactory.createFromNickName(node.Name, "DynamicRuleRegister", (XmlElement)node);
						Application.currentApp.Log.println(Application.NORMAL_STARTUP_LOG_LEVEL, "DynamicBizObjRegistration enabled to use ICall {0}", node.OuterXml);
						break;
					}
			}
		}

		/// <summary>
		/// Creates an IBizObjList based on the business object type passed into the method.
		/// The <c>SourceType</c> defaults to <c>SourceType.BasicSource</c>,  
		/// no search criteria is used to determine the business objects in the list,
		/// and no initialization text is specified.
		/// </summary>
		/// <param name="objectType">A business object type defined in the application configuration file.</param>
		/// <returns>A newly created <c>IBizObjList</c>.</returns>
		public IBizObjList createList(string objectType)
		{
			IBizObjList bizObjList = this.createList(SourceType.BasicSource, objectType);
			return bizObjList;
		}

		/// <summary>
		/// Creates an IBizObjList based on the <c>SourceType</c> and business object type passed into the method.
		/// No search criteria will be used to determine the business objects in the list, 
		/// and no initialization text is specified. Use this method to create an <c>IBizObjList</c>
		/// when the <c>SourceType.BasicSource</c> is not appropriate.
		/// </summary>
		/// <param name="srcType">The <c>SourceType</c> to use for the business object list.</param>
		/// <param name="objectType">A business object type defined in the application configuration file.</param>
		/// <returns>A newly created <c>IBizObjList</c>.</returns>
		public IBizObjList createList(SourceType srcType, string objectType)
		{
			IBizObjList bizObjList = this.createList(srcType, "", objectType, null);
			return bizObjList;
		}

		/// <summary>
		/// Creates an IBizObjList based on the specified <c>SourceType</c>, business object type,
		/// implementation-specfic search criteria, and initialization text.
		/// </summary>
		/// <param name="srcType">The <c>SourceType</c> to use for the business object list.</param>
		/// <param name="sourceInfo">Initialization text used by the data source to get ready to work properly. 
		/// An XML string that loads into a DOM if the type is SourceType.XmlSource. Can be an empty string.</param>
		/// <param name="objectType">A business object type defined in the application configuration file.</param>
		/// <param name="listInfo">Implementation-specfic search criteria that determines which business objects 
		/// should be included in the list. Can be null if no specific search criteria is necessary.</param>
		/// <returns>A newly created <c>IBizObjList</c>.</returns>
		public IBizObjList createList(SourceType srcType, string sourceInfo, string objectType, string listInfo)
		{
			IBizObjSource bizObjSource = this.createSource(srcType, sourceInfo);
			IBizObjList bizObjList = bizObjSource.getList(objectType, listInfo);
			return bizObjList;
		}

		public IBizObjIterator getIterator(string iteratorSetName)
		{
			IBizObjIterator i = s_emptyIterator;
			if (m_iterators != null && !m_iterators.TryGetValue(iteratorSetName, out i))
				i = s_emptyIterator;
			if (i == s_emptyIterator)
			{
				IClassFactory iteratorFactory = Application.currentApp.factories[iteratorSetName];
				if (iteratorFactory != null)
				{
					IteratorSet iset = new IteratorSet();
					foreach (string iname in iteratorFactory.entries.Keys)
						iset.addIterator((IBizObjIterator)iteratorFactory.createFromNickName(iname));
					if (m_iterators == null)
						m_iterators = new Dictionary<string, IBizObjIterator>();
					m_iterators[iteratorSetName] = i = iset;
				}
			}
			return i;
		}
	}
}
#endregion 